package logic

import (
	"bytes"
	"errors"
	_html "github.com/alecthomas/chroma/formatters/html"
	toc "github.com/mdigger/goldmark-toc"
	"github.com/yuin/goldmark"
	highlighting "github.com/yuin/goldmark-highlighting"
	"github.com/yuin/goldmark/extension"
	"go.uber.org/zap"
	"html"
	"struggle/dao/mysql"
	"struggle/model"
	"struggle/pkg/snowflake"
)

var (
	ErrInvalidQuestionID = errors.New("无效的问题id")
)

func CreateAnswer(answer *model.Answer) (err error) {
	// 1. 针对 content 做一个转义，防止 xss 攻击
	answer.Content = html.EscapeString(answer.Content)
	//  TODO：敏感词过滤
	// 2. 校验 questionID 是否存在
	if ok := mysql.CheckQuestionIDExist(answer.QuestionID); !ok {
		err = ErrInvalidQuestionID
		zap.L().Warn("invalid question id", zap.Uint64("questionID", answer.QuestionID))
		return
	}
	// 3. 生成回答的 id
	answerID, err := snowflake.GenID()
	if err != nil {
		zap.L().Error("snowflake.GenID failed", zap.Error(err))
		return
	}
	answer.AnswerID = answerID
	// 4. 将回答写入数据库
	err = mysql.CreateAnswer(answer)
	if err != nil {
		zap.L().Error("mysql.CreateAnswer failed", zap.Error(err))
		return
	}
	return
}

func GetAnswerList(questionID uint64, offset, limit int) (apiAnswerList *model.ApiAnswerList, err error) {
	answerList, err := mysql.GetAnswerList(questionID, offset, limit)
	if err != nil {
		zap.L().Error("mysql.GetAnswerList failed", zap.Error(err))
		return
	}
	if len(answerList) == 0 {
		return
	}

	var userIDList []uint64
	for _, v := range answerList {
		userIDList = append(userIDList, v.AuthorID)
	}

	userInfoList, err := mysql.GetUserInfoList(userIDList)
	if err != nil {
		zap.L().Error("mysql.GetUserInfoList failed", zap.Any("userIDList", userIDList), zap.Error(err))
		return
	}

	apiAnswerList = &model.ApiAnswerList{
		AnswerList: make([]*model.ApiAnswer, 0, len(answerList)),
	}
	for _, v := range answerList {
		apiAnswer := &model.ApiAnswer{}
		apiAnswer.Answer = *v
		// 自定义解析器
		markdown := goldmark.New(
			goldmark.WithExtensions(extension.GFM), // 支持 GFM
			goldmark.WithExtensions( // 语法高亮插件
				highlighting.NewHighlighting(
					highlighting.WithStyle("monokai"),
					highlighting.WithFormatOptions(
						_html.WithLineNumbers(true)))),
		)
		var buf bytes.Buffer
		convertFunc := toc.Markdown(markdown)
		_, err = convertFunc([]byte(apiAnswer.Answer.Content), &buf)
		if err != nil {
			zap.L().Error("服务端解析 Markdown 错误", zap.Error(err))
			return nil, err
		}
		apiAnswer.Answer.HTML = buf.String()

		for _, user := range userInfoList {
			if user.UserSn == v.AuthorID {
				apiAnswer.AuthorName = user.Email
				break
			}
		}

		apiAnswerList.AnswerList = append(apiAnswerList.AnswerList, apiAnswer)
	}

	count, err := mysql.GetAnswerCount(questionID)
	if err != nil {
		zap.L().Error("mysql.GetAnswerCount failed", zap.Uint64("questionID", questionID), zap.Error(err))
		return
	}

	apiAnswerList.TotalCount = count
	return
}

// 获取个人用户回答列表
func GetUserAnswerList(sn uint64, offset, limit int) (apiUserAnswerList *model.ApiUserAnswerList, err error) {
	userAnswerList, err := mysql.DefaultUserCount.FindAnswer(sn, offset, limit)
	if err != nil {
		zap.L().Error("mysql.GetUserQuestionList failed", zap.Error(err))
		return
	}
	if len(*userAnswerList) == 0 {
		zap.L().Info("question list is null")
		return
	}

	count, err := mysql.GetUserAnswerCount(sn)
	if err != nil {
		zap.L().Error("mysql.GetUserQuestionCount failed", zap.Error(err))
		return
	}
	apiUserAnswerList = &model.ApiUserAnswerList{}
	apiUserAnswerList.AnswerList = userAnswerList
	apiUserAnswerList.TotalCount = count
	return
}
